package com.ziytek.web.citizen.service;

import com.google.gson.Gson;
import com.ziytek.web.citizen.caffeine.CachePutOrGet;
import com.ziytek.web.citizen.config.caffeine.CaffeineUtil;
import com.ziytek.web.citizen.constant.NumConstant;
import com.ziytek.web.citizen.mapper.BoardConfigMapper;
import com.ziytek.web.citizen.model.bean.CapStatsVo;
import com.ziytek.web.citizen.model.vo.OperationVo;
import com.ziytek.web.citizen.model.vo.RealTimeDo;
import com.ziytek.web.citizen.model.vo.RealTimeVo;
import com.ziytek.web.citizen.util.KeyUtil;
import com.ziytek.web.citizen.util.StringUtil;
import com.ziytek.web.citizen.virtual.VirtualThreadPool;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;

import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.util.*;
import java.util.concurrent.CountDownLatch;

/**
 * @author fenghx
 */
@Slf4j
@Service
@RequiredArgsConstructor(onConstructor = @__(@Autowired))
public class BatteryStatsService {
    private final Gson gson;
    private final DoorsService doorsService;
    private final CachePutOrGet cachePutOrGet;
    private final BoardConfigMapper boardConfigMapper;
    private final CompressionService compressionService;
    private final OperationVoCacheService operationVoCacheService;

    public RealTimeDo getSignalRealTimeVo(int cabinetNo, int boardNo, int channelNo) {
        var cacheKey = cabinetNo + CaffeineUtil.CACHE_KEY_SYMBOL + boardNo + CaffeineUtil.CACHE_KEY_SYMBOL + channelNo;
        var stats = cachePutOrGet.getStatsCache(cacheKey);
        var opKey = KeyUtil.commandOperateKey(cabinetNo, boardNo, channelNo);
        var vo = operationVoCacheService.getOperationVo(opKey);
        if (vo != null && stats != null && !vo.getSetType().equalsIgnoreCase(stats.cty)) {
            log.info("统计容量数据类型不匹配,cabinetNo:{},boardNo:{},channelNo:{},type:{},cty:{}", cabinetNo, boardNo, channelNo, vo.getSetType(), stats.cty);
            stats.cty = vo.getSetType();
        }

        var realTimeDo = getRealTimeVo(stats, String.valueOf(cabinetNo));
        if (realTimeDo != null && vo != null && StringUtil.isNotEmpty(vo.getBatchNo())) {
            // 获取批次号
            realTimeDo.setBatchNo(vo.getBatchNo());
        }
        return realTimeDo;
    }

    public Map<String, List<RealTimeDo>> getBatteryStats(String cabinetNo, List<CapStatsVo> statsList) {
        if (CollectionUtils.isEmpty(statsList)) {
            log.info("getBatteryStats Stats is null for cabinetNo: {}", cabinetNo);
            return allEmptyRealTime(Integer.parseInt(cabinetNo));
        }

        Map<String, List<RealTimeDo>> map = new HashMap<>();
        CountDownLatch latch = new CountDownLatch(1);

        Runnable task = () -> {
            try {
                for (var stats : statsList) {
                    var vo = getRealTimeVo(stats, cabinetNo);
                    if (vo == null) {
                        continue;
                    }
                    var key = vo.getCn() + CaffeineUtil.CACHE_KEY_CONNECTION_SYMBOL + vo.getBn();
                    List<RealTimeDo> realTimeVos = map.get(key);
                    if (CollectionUtils.isEmpty(realTimeVos)) {
                        realTimeVos = new ArrayList<>();
                    }
                    realTimeVos.add(vo);
                    realTimeVos.sort(Comparator.comparingInt(RealTimeDo::getCnn));
                    map.put(key, realTimeVos);
                }
            } catch (Exception e) {
            } finally {
                latch.countDown();
            }
        };

        VirtualThreadPool.execute(task);

        try {
            // 等待任务完成
            latch.await();
        } catch (Exception e) {
            log.error("异常", e);
        }

        responseMap(map);

        return map;
    }

    private void responseMap(Map<String, List<RealTimeDo>> map) {
        if (CollectionUtils.isEmpty(map)) {
            return;
        }
        List<Integer> channelList = new ArrayList<>();
        channelList.add(NumConstant.INT_ONE);
        channelList.add(NumConstant.INT_TWO);
        channelList.add(NumConstant.INT_THREE);
        channelList.add(NumConstant.INT_FOUR);
        channelList.add(NumConstant.INT_FIVE);
        channelList.add(NumConstant.INT_SIX);
        channelList.add(NumConstant.INT_SEVEN);
        channelList.add(NumConstant.INT_EIGHT);
        // 补传所有电池的实时数据
        for (var key : map.keySet()) {
            List<RealTimeDo> realTimeVos = map.get(key);
            if (CollectionUtils.isEmpty(realTimeVos) || realTimeVos.size() == NumConstant.INT_EIGHT) {
                continue;
            }
            var fvo = realTimeVos.getFirst();
            for (var i : channelList) {
                boolean has = realTimeVos.stream().anyMatch(r -> r.getCnn() == i);
                if (has) {
                    continue;
                }
                var ro = getEmptyRealTimeDo(fvo.getCn(), fvo.getBn(), i, 2, 0, 0);
                realTimeVos.add(ro);
            }

            map.put(key, realTimeVos);
        }
    }

    public RealTimeDo getRealTimeVo(CapStatsVo stats, String cabinetNo) {
        if (stats == null) {
            return null;
        }

        var sp = stats.k.split(CaffeineUtil.CACHE_KEY_SYMBOL);
        var boardNo = sp[NumConstant.INT_ONE];
        var channelNo = sp[NumConstant.INT_TWO];
        var vdo = new RealTimeDo();
        var vo = new RealTimeVo();
        vo.setT(stats.getT());
        vo.setVo(stats.getVo());
        vo.setTs(stats.getTs());
        vo.setAc(stats.getAc());
        vo.setCpa(stats.getC());
        vo.setEt(stats.getEt());
        vo.setCty(stats.getCty());
        vo.setC(Math.abs(stats.getI()));
        vo.setV(Math.abs(stats.getV()));
        vo.setCn(Integer.parseInt(cabinetNo));
        vo.setBn(boardNo == null ? NumConstant.INT_ZERO : Integer.parseInt(boardNo));
        vo.setCnn(channelNo == null ? NumConstant.INT_ZERO : Integer.parseInt(channelNo));
        byte[] bytes = gson.toJson(vo).getBytes(StandardCharsets.UTF_8);

        try {
            byte[] data = compressionService.compress(bytes);
            vdo.setData(data);
        } catch (IOException e) {
            throw new RuntimeException(e);
        }

        vdo.setBn(vo.getBn());
        vdo.setCn(vo.getCn());
        vdo.setCnn(vo.getCnn());
        vdo.setStatus(stats.getT());
        vdo.setCreateTime(System.currentTimeMillis());
        return vdo;
    }


    private Map<String, List<RealTimeDo>> allEmptyRealTime(int cabinetNo) {
        var doors = doorsService.getDoorsConfig();
        List<String> keyList = new ArrayList<>();
        for (var d : doors) {
            var boardAddress = boardConfigMapper.queryByDoorListNum(d.getDoor());
            if (boardAddress == null) {
                continue;
            }
            for (int boardNo : boardAddress) {
                keyList.add(cabinetNo + CaffeineUtil.CACHE_KEY_CONNECTION_SYMBOL + boardNo);
            }
        }
        Map<String, List<RealTimeDo>> map = new HashMap<>();

        List<Integer> channelList = new ArrayList<>();
        channelList.add(NumConstant.INT_ONE);
        channelList.add(NumConstant.INT_TWO);
        channelList.add(NumConstant.INT_THREE);
        channelList.add(NumConstant.INT_FOUR);
        channelList.add(NumConstant.INT_FIVE);
        channelList.add(NumConstant.INT_SIX);
        channelList.add(NumConstant.INT_SEVEN);
        channelList.add(NumConstant.INT_EIGHT);

        for (var k : keyList) {
            for (int channelNo : channelList) {
                int bn = Integer.parseInt(k.split(CaffeineUtil.CACHE_KEY_CONNECTION_SYMBOL)[1]);
                var vo = getEmptyRealTimeDo(cabinetNo, bn, channelNo, 2, 0, 0);
                if (vo == null) {
                    continue;
                }
                List<RealTimeDo> vos = map.get(k);
                if (CollectionUtils.isEmpty(vos)) {
                    vos = new ArrayList<>();
                }
                vos.add(vo);
                vos.sort(Comparator.comparingInt(RealTimeDo::getCnn));
                map.put(k, vos);
            }
        }

        return map;
    }

    public RealTimeDo getEmptyRealTimeDo(int cabinetNo, int boardNo, int channelNo, int type, int current, int v) {
        var opKey = KeyUtil.commandOperateKey(cabinetNo, boardNo, channelNo);
        OperationVo vo = operationVoCacheService.getOperationVo(opKey);

        var commandType = vo != null ? vo.getSetType() : "sl";

        var rd = new RealTimeDo();
        rd.setBn(boardNo);
        rd.setCn(cabinetNo);
        rd.setCnn(channelNo);
        rd.setData(getRealTime(commandType, cabinetNo, boardNo, channelNo, type, current, v));
        rd.setCreateTime(System.currentTimeMillis());
        return rd;
    }

    private byte[] getRealTime(String commandType, int cabinetNo, int boardNo, int channelNo, int type, int c, int v) {

        var dataStr = "{\"data\":[40,-75,47,-3,96,94,0,-67,6,0,98,-51,39,25,112,121,3,24,-120,-84,-88,-93,-46,-99,36,91,-4,-46,85,94,117,41,-83,-8,-66,-12,-1,13,1,91,-10,-17,-21,-124,-73,15,-85,-90,26,-82,99,-32,-79,-98,-77,-29,-20,117,27,51,115,-16,-10,-89,-41,-54,60,-68,-123,32,-25,-81,94,-73,88,119,-17,-69,19,77,-23,-33,45,-81,-78,-57,-89,104,74,22,109,-86,-53,74,-41,45,-17,91,-38,-68,79,41,66,-53,-14,42,101,-30,83,98,100,-123,52,-82,-37,-24,7,23,-60,97,-56,-122,18,-58,33,50,114,16,4,31,-120,19,-116,20,61,-12,78,42,61,-84,94,-47,117,-63,91,-22,106,-90,-55,123,29,-65,-115,-32,-92,124,-123,111,-83,27,-114,-32,-83,92,42,127,107,-75,-73,20,91,45,23,40,48,66,-89,88,15,16,40,13,121,-109,-64,73,-56,27,31,114,-87,47,84,95,-122,5,-118,-116,-114,124,111,-34,-80,64,18,8,25,0,-18,97,-74,-128,-69,17,-104,52,-111,-115,28,-76,104,5,-33,21,123],\"cn\":0,\"bn\":0,\"cnn\":1,\"createTime\":1742520797716,\"time\":0}";
        var rdo = gson.fromJson(dataStr, RealTimeDo.class);

        try {
            byte[] dataOne = compressionService.decompress(rdo.getData());
            var vo = gson.fromJson(new String(dataOne), RealTimeVo.class);
            vo.setT(type);
            vo.setEt(1);
            vo.setC(c);
            vo.setV(v);
            vo.setCty(commandType);
            vo.setCn(cabinetNo);
            vo.setBn(boardNo);
            vo.setCnn(channelNo);
            vo.setTs(0);
            vo.setVo(null);
            byte[] bytes = gson.toJson(vo).getBytes(StandardCharsets.UTF_8);
            return compressionService.compress(bytes);
        } catch (IOException e) {
            log.error("compress error", e);
        }

        return new byte[0];
    }
}
